#include "config.h"
#include "..\utils\crc32.h"
//static Config_t conf;

const   Config_t temp = {
    0,                    //Crc32
    HARDWARE_ID,           //HardwareId
    DEVICE_ID,            //DeviceId
//    {4800},               //Com0
//    {4800},               //Com1
//    {9600},               //Com2
    {
      19200,
      30000, 
      {
        {"internet.mts.ru", "89.110.55.250", 23000, 5003}, //Sim[0]
        {"internet.beeline.ru", "89.110.55.250", 23000, 5003}  //Sim[1]
      }
    },                   //Network
//    {-1, -1, -1, -1, -1},      //Schedule
//    {{1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0}} //AIN Calibration
  1, // Update
  0
  };  

/*
const   Config_t temp = {
    0,                    //Crc32
    HARDWARE_ID,           //HardwareId
    DEVICE_ID,            //DeviceId
//    {4800},               //Com0
//    {4800},               //Com1
//    {9600},               //Com2
    {
      19200,
      30000, 
      {
        {"her", "gopa", 99999, 666}, //Sim[0]
        {"nax", "pezda", 99999, 777}  //Sim[1]
      }
    },                   //Network
//    {-1, -1, -1, -1, -1},      //Schedule
//    {{1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0}} //AIN Calibration
  1 // Update
  };  

*/


const CFGParameter  CP[]={
  {"APN1",CFGStringParameter,APN_NAME_MAX_LEN-1},
  {"SERVER1",CFGStringParameter,SERVER_NAME_MAX_LEN-1},
  {"PORT1",CFGUnsignedIntParameter,4},
  {"APN2",CFGStringParameter,APN_NAME_MAX_LEN-1},
  {"SERVER2",CFGStringParameter,SERVER_NAME_MAX_LEN-1},
  {"PORT2",CFGUnsignedIntParameter,4},
  {"UPDATE",CFGUnsignedIntParameter,4}
};


unsigned int  GetConfigCRC32(Config_t *cfg){
  unsigned int ret;
  unsigned int i;
  ret=0xAAAA5555;
  for(i=0;i<(sizeof(Config_t)-sizeof(unsigned int));i++){// побайтно считаю CRC по конфигу без последнего поля (crc32)
    ret=Crc32Step( ((unsigned char*)cfg)[i],ret);
  }
  return ret;
}


void config_init(Config_t *cfg){
  memcpy(cfg, &temp, sizeof(Config_t));
}

/*
bool config_save_eeprom()
{
  unsigned int sector_index = getSectorIndex(CONFIG_ADDRESS);
  if (sector_index == INVALID_RESULT)
    return false;
  unsigned long sector_size  = (getSectorSize(sector_index) << 10);
  if (iap_erase(sector_index)) return false;

  char buffer[CONFIG_LENGTH] __attribute__((aligned (4)));
  memcpy(buffer, CONFIG, sizeof(Config_t));
  if (iap_program(sector_index, CONFIG_ADDRESS, buffer, sector_size) != 0)
    return false;
  return true;
}

void config_load_eeprom()
{
  if (CONFIG != NULL) memcpy(CONFIG, (char*)CONFIG_ADDRESS, sizeof(Config_t));
}
*/

bool config_load_bin(Config_t *cfg){// прежняя функция чтения конфига от русского проекта
  return true;
  FIL file;
  if (cfg == NULL) return false;
  config_load_eeprom();
  unsigned int crc32 = cfg->Crc32;
  if (f_open(&file, CONFIG_FILE, FA_OPEN_EXISTING | FA_READ) != FR_OK)
    return false;
  if (file.fsize == sizeof(Config_t)){
    unsigned int br = 0;
    if (f_read(&file, cfg, sizeof(Config_t), &br) == FR_OK){
      cfg->Crc32    = crc32;
      cfg->DeviceId = DEVICE_ID;
      cfg->Hardware = HARDWARE_ID;
      f_close(&file);
//      assert(config_save_eeprom() == true);
      return true;
    }
  }
  f_close(&file);
  return false;
}

//8**************************************************************************************
// функция чтения файла конфигурации бутлоадера
// параметры: cfg - куда складываем конфигурацию, bf - заранее выделенный буфер для чтения файла, sz - размер этого буфера
// возвращает 0 если произошла ошибка, иначе парсит конфиг и возвращает длину файла конфига
//
unsigned int config_load(Config_t *cfg, unsigned char *bf, unsigned int sz){
  FIL file;
  bool  parse;
  unsigned int br=0;
  if ((cfg == NULL)||(bf==NULL)||(sz<512)) return 0;
  if (f_open(&file, CONFIG_FILE, FA_OPEN_EXISTING | FA_READ) != FR_OK)  // открыл файл
    return 0;
  sz--;
  parse=false;
  if(sz<file.fsize)sz=file.fsize; // определил количество байт для чтения
  if (f_read(&file, bf, sz, &br) == FR_OK){// прочитал в буфер
    if((br>MIN_CFGFILE_LEN)&&(br<=sz)){ // количество прочитанных байт корректно

  bf[br]=0;// в конец буфера запихнул 0 чтоб контролировать границу
  parse=FindParameter(bf,&CP[0],cfg->Network.Sim[0].APN);
  parse&=FindParameter(bf,&CP[1],cfg->Network.Sim[0].Server);
  parse&=FindParameter(bf,&CP[2],&(cfg->Network.Sim[0].HttpPort));
  parse&=FindParameter(bf,&CP[3],cfg->Network.Sim[1].APN);
  parse&=FindParameter(bf,&CP[4],cfg->Network.Sim[1].Server);
  parse&=FindParameter(bf,&CP[5],&(cfg->Network.Sim[1].HttpPort));
  parse&=FindParameter(bf,&CP[6],&(cfg->Update));

//      parse=ParseConfig(cfg,bf,br);
    }
  }
  f_close(&file);
  if(!parse)br=0;
  return br;
}

//8****************************************************************************
//
/*
bool ParseConfig(Config_t *cfg, unsigned char *bf, unsigned int br){
  bool  parse;
  bf[br]=0;// в конец буфера запихнул 0 чтоб контролировать границу
  parse=FindParameter(bf,&CP[0],cfg->Network.Sim[0].APN);
  parse&=FindParameter(bf,&CP[1],cfg->Network.Sim[0].Server);
  parse&=FindParameter(bf,&CP[2],&(cfg->Network.Sim[0].HttpPort));
  parse&=FindParameter(bf,&CP[3],cfg->Network.Sim[1].APN);
  parse&=FindParameter(bf,&CP[4],cfg->Network.Sim[1].Server);
  parse&=FindParameter(bf,&CP[5],&(cfg->Network.Sim[1].HttpPort));
  parse&=FindParameter(bf,&CP[6],&(cfg->Update));
  return parse;
}
*/

bool  StoreConfig(Config_t *cfg, unsigned char *bf, unsigned int bfl){// записывает конфигурацию в файл
  FIL file;
  FRESULT res;
  unsigned int pt;
  unsigned int wl;
  if (f_open(&file, CONFIG_FILE, FA_CREATE_ALWAYS | FA_WRITE) != FR_OK)return false;  // чего то сдохло совсем
  pt=0;

  pt=snprintf(bf,15,"UPDATE:1\r\n");
  pt+=snprintf(bf+pt,APN_NAME_MAX_LEN+9,"APN1: %s\r\n",cfg->Network.Sim[0].APN);
  pt+=snprintf(bf+pt,SERVER_NAME_MAX_LEN+12,"SERVER1: %s\r\n",cfg->Network.Sim[0].Server);
  pt+=snprintf(bf+pt,15,"PORT1: %i\r\n",cfg->Network.Sim[0].HttpPort);
  pt+=snprintf(bf+pt,APN_NAME_MAX_LEN+9,"APN2: %s\r\n",cfg->Network.Sim[1].APN);
  pt+=snprintf(bf+pt,SERVER_NAME_MAX_LEN+12,"SERVER2: %s\r\n",cfg->Network.Sim[1].Server);
  pt+=snprintf(bf+pt,15,"PORT2: %i\r\n",cfg->Network.Sim[1].HttpPort);
  res=f_write(&file,bf,pt,&wl);
  f_close(&file);
  if(wl==pt)return true;
  else return false;

  
}


//8****************************************************************************
// функция поиска параметра в файле
// параметры: bf: буфер с прочитанным файлом, в конце буфера 0. tf - описание параметра для поиска, dest - куда складываю параметр
//
//
//
bool FindParameter(unsigned char *bf, const CFGParameter *tf, void *dest){
  unsigned int i=0;
  unsigned int l;
  unsigned int r;
  bool  ret=false;
  while(bf[i]){// поиск имени параметра в начале строки
    l=CmpParam(bf+i,tf->name);  // сравнивает начало строки с искомым параметром
    i+=l;
    if(l){// параметр найден, сохраняю
      if(bf[i]==':'){ // после имени должно быть ':'
        i++;
        switch(tf->type){// в зависимости от типа параметра сохраняю его
          case CFGStringParameter:
            r=StoreStringParameter(bf+i,dest,tf->len,&l);
//            if((r)&&(r==l))ret=true;  // параметр нашли и сохранили
            if(r)ret=true;  // параметр нашли и сохранили
          break;
          case CFGUnsignedIntParameter:
            if(StoreIntParameter(bf+i,dest,tf->len,&l))ret=true;
          break;
        }
      }
      break;  // после нахождения параметра выход из поиска
    }
    i+=FindNextString(bf+i);
  }


  return ret;
}

//8******************************************************************************
// сравнивает две строки до окончания одной из них, в первой строке (S1) пропускаются начальные пробелы
// если совпадение то возвращается длинна совпавшей части, если нет то возвращается 0
//
unsigned int  CmpParam(const unsigned char *s1,const unsigned char *s2){
  unsigned int ret=0; // индекс в буфере
  unsigned int dix=0; // индекс в параметре
  while(s1[ret]==' ')ret++; // пропускаю пробелы
  while((s1[ret])&&(s2[dix])){// цикл пока не встречу 0
    if(s1[ret]!=s2[dix]){// сравниваю строки, если не сравнились то выхожу с 0
      ret=0;
      break;
    }
    ret++;
    dix++;
  }
  // сюда пришел когда либо строки не сравнились и ret=0 либо когда в какой то строке встретился 0 но строки сравнились, ret!=0
  return ret;
}

//8******************************************************************************
// ищет следующее вхождение в строке символов 0x0d 0x0a либо одного из них
// возвращает количество байт с начала строки вместе с этими символами
// (s+RetVal указывает на начало следующей строки (или 0 если буфер закончился)
unsigned int  FindNextString(const unsigned char *s){
  unsigned int i=0;
  while(s[i]){// сначала ищу первое вхождение 0x0D или 0x0A
    if( (s[i]==0x0d)||(s[i]==0x0a))break;
    i++;
  }
  while(s[i]){// теперь цикл пока они все (0x0d, 0x0A) закончатся
    if( (s[i]!=0x0d)&&(s[i]!=0x0a))break;
    i++;
  }
  return i;
}

//8********************************************************************
// сохраняет строковый параметр 
// параметры: bf - буфер с параметром, dest - куда класть параметр, maxlen - максимальное количество байт для сохранения
//            load - сюда кладется количество байт, взятых из буфера
// возвращает: количество сохраненных в приемнике байт, если 0 то ошибка
unsigned int  StoreStringParameter(const unsigned char *bf, unsigned char *dest, unsigned int maxlen, unsigned int *load){
  unsigned int ret=0; // индекс в источнике
  unsigned int  dix=0;  // индекс в приемнике

  while(bf[ret]){
    if( (bf[ret]==0x0d)||(bf[ret]==0x0d)||(bf[ret]==';') )break;  // конец строки или начало комментария
    if( (bf[ret]>' ') ){  // если нормальный символ то переносим приемник
      dest[dix]=bf[ret];
      dix++;
    }
    ret++;
    if(dix>=maxlen)break; // контроль превышения длины
  }
  if(dix)dest[dix]=0; // завершающий 0 в сохраненную строку
  *load=ret;
  return dix;
}

//8********************************************************************
// сохраняет целочисленный параметр 
// параметры: bf - буфер с параметром, dest - куда класть параметр, maxlen - максимальное количество байт для сохранения
//            load - сюда кладется количество байт, взятых из буфера
// возвращает: количество сохраненных в приемнике байт, если 0 то ошибка
bool StoreIntParameter(const unsigned char *bf, unsigned int *dest, unsigned int maxlen, unsigned int *load){
  unsigned int i;
//  unsigned int sign;    // возможная позиция знака числа
  unsigned int  res=0;  // накопление результата преобразования
//  unsigned int  mull=1; // множитель
  unsigned char sym;    // выделяем обрабатываемый символ
  unsigned char cut;    // максимальное допустимое количество символов в числе
  bool  ret=false;
  i=0;
  while(bf[i]==' ')i++; // пропускаю пробелы

//  if( (bf[i]=='-')||(bf[i]=='+')){
//    sign=i;  // позиция знака
//    i++;
//  }

  if( (bf[i]=='0')&&(bf[i+1]=='x') ){ // HEX
    i+=2;
    cut=8;
    while((bf[i])&&cut){
      sym=bf[i];
      if(sym>=97)sym-=32; // переводим символ в верхний регистр
      sym-='0';
      if( (sym<=9)||((sym>=17)&&(sym<=22))){// корректный символ
        if(sym>9)sym-=7;
        res<<=4;
        res|=sym;
        cut--;
        i++;
      }else break;
    }
    if(cut!=8)ret=true; // если было хоть одно преобразование
  }else{  // DEC
    cut=9;
    while((bf[i])&&cut){
      if( (bf[i]>='0')&&(bf[i]<='9')){// если символ правильный то обрабатываю
        sym=bf[i];
        sym-='0';   // символ=>в число
        res*=10;
        res+=sym;
        cut--;
        i++;
      }else break;
    }
    if(cut!=9)ret=true; // если было хоть одно преобразование
  }
  if( (bf[i]!=' ')&&(bf[i]!=0x0D)&&(bf[i]!=0x0A)&&(bf[i]!=0)&&(bf[i]!=';') )ret=false;  // после числа может быть только пробел, начало комментария, перевод строки или конец файла
  if(ret){  // возвращаю корректное преобразование
    *dest=res;
  }
  *load=i;
  return  ret;
}